package com.wsleo.core.db;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.DbUtils;

import com.wsleo.common.utils.BeanUtils;
import com.wsleo.common.utils.Inflector;
import com.wsleo.core.cache.CacheManager;

/**
 * 数据库对象的基类
 */
public class POJO implements Serializable {

    private static final long serialVersionUID = 1L;

    protected final static transient char OBJ_COUNT_CACHE_KEY = '#';
    private final static String TABLE_PREFIX = "tb_";
    private String tableName;

    public static void evictCache(String cache, Serializable key) {
        CacheManager.evict(cache, key);
    }

    public static void setCache(String cache, Serializable key,
            Serializable value) {
        CacheManager.set(cache, key, value);
    }

    public static Object getCache(String cache, Serializable key) {
        return CacheManager.get(cache, key);
    }

    /**
     * 分页列出所有对象
     * 
     * @param page
     * @param size
     * @return
     */
    public List<? extends POJO> List(int page, int size) {
        String sql = "SELECT * FROM " + TableName() + " ORDER BY id DESC";
        return QueryHelper.query_slice(getClass(), sql, page, size);
    }

    public List<? extends POJO> Filter(String filter, int page, int size) {
        String sql = "SELECT * FROM " + TableName() + " WHERE " + filter
                + " ORDER BY id DESC";
        return QueryHelper.query_slice(getClass(), sql, page, size);
    }

    /**
     * 统计此对象的总记录数
     * 
     * @return
     */
    public int TotalCount(String filter) {
        return (int) QueryHelper.stat("SELECT COUNT(*) FROM " + TableName() + " WHERE " + filter);
    }

    /**
     * 返回默认的对象对应的表名
     * 
     * @return
     */
    protected String TableName() {
        if (tableName == null)
            tableName = TABLE_PREFIX
                    + Inflector.getInstance().underscore(getClass());
        return tableName;
    }

    /**
     * 返回对象对应的缓存区域名
     * 
     * @return
     */
    public String CacheRegion() {
        return this.getClass().getSimpleName();
    }

    /**
     * 是否根据ID缓存对象，此方法对Get(long id)有效
     * 
     * @return
     */
    protected boolean IsObjectCachedByID() {
        return false;
    }

    /**
     * 插入对象到数据库表中
     * 
     * @return
     */
    public long Save() {
        if (getId() > 0) {
            _InsertObject(this);
        } else {
            setId(_InsertObject(this));
        }
        if (this.IsObjectCachedByID()) {
            CacheManager.evict(CacheRegion(), OBJ_COUNT_CACHE_KEY);
        }
        return getId();
    }

    /**
     * 根据id主键删除对象
     * 
     * @return
     */
    public boolean Delete() {
        boolean dr = Evict(QueryHelper.update("DELETE FROM " + TableName()
                + " WHERE id=?", getId()) == 1);
        if (dr) {
            CacheManager.evict(CacheRegion(), OBJ_COUNT_CACHE_KEY);
        }
        return dr;
    }

    /**
     * 根据条件决定是否清除对象缓存
     * 
     * @param er
     * @return
     */
    public boolean Evict(boolean er) {
        if (er && IsObjectCachedByID())
            CacheManager.evict(CacheRegion(), getId());
        return er;
    }

    /**
     * 清除指定主键的对象缓存
     * 
     * @param obj_id
     */
    protected void Evict(long obj_id) {
        CacheManager.evict(CacheRegion(), obj_id);
    }

    public POJO Get(java.math.BigInteger id) {
        if (id == null)
            return null;
        return Get(id.longValue());
    }

    /**
     * 根据主键读取对象详细资料，根据预设方法自动判别是否需要缓存
     * 
     * @param id
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T extends POJO> T Get(long id) {
        if (id <= 0)
            return null;
        String sql = "SELECT * FROM " + TableName() + " WHERE id=?";
        boolean cached = IsObjectCachedByID();
        return (T) QueryHelper.read_cache(getClass(), cached ? CacheRegion()
                : null, id, sql, id);
    }

    public List<? extends POJO> BatchGet(List<Long> ids) {
        if (ids == null || ids.size() == 0)
            return null;
        
        List<? extends POJO> beans = QueryHelper.query(getClass(),
                _Get_BatchGet_Sql(ids), ids.toArray(new Object[ids.size()]));
        if (IsObjectCachedByID()) {
            for (Object bean : beans) {
                CacheManager.set(CacheRegion(), ((POJO) bean).getId(),
                        (Serializable) bean);
            }
        }
        return beans;
    }
    
    private String _Get_BatchGet_Sql(List<Long> ids) {
        StringBuilder sql = new StringBuilder("SELECT * FROM " + TableName() + " WHERE id IN (");
        for (int i = 1; i <= ids.size(); i++) {
            sql.append('?');
            if (i < ids.size())
                sql.append(',');
        }
        sql.append(')');
        return sql.toString();
    }

    /**
     * 统计此对象的总记录数
     * 
     * @return
     */
    public int TotalCount() {
        String sql = "SELECT COUNT(*) FROM " + TableName();
        if (this.IsObjectCachedByID())
            return (int) QueryHelper.stat_cache(CacheRegion(),
                    OBJ_COUNT_CACHE_KEY, sql);
        return (int) QueryHelper.stat(sql);
    }

    /**
     * 批量加载项目
     * 
     * @param pids
     * @return
     */
    @SuppressWarnings("rawtypes")
    public List LoadList(List<Long> p_pids) {
        if (p_pids == null)
            return null;
        final List<Long> pids = new ArrayList<Long>(p_pids.size());
        for (Number obj : p_pids) {
            pids.add(obj.longValue());
        }
        String cache = this.CacheRegion();
        List<POJO> prjs = new ArrayList<POJO>(pids.size()) {
            private static final long serialVersionUID = 1L;
            {
                for (int i = 0; i < pids.size(); i++)
                    add(null);
            }
        };
        List<Long> no_cache_ids = new ArrayList<Long>();
        for (int i = 0; i < pids.size(); i++) {
            long pid = pids.get(i);
            POJO obj = (POJO) CacheManager.get(cache, pid);

            if (obj != null)
                prjs.set(i, obj);
            else {
                no_cache_ids.add(pid);
            }
        }

        if (no_cache_ids.size() > 0) {
            List<? extends POJO> no_cache_prjs = BatchGet(no_cache_ids);
            if (no_cache_prjs != null)
                for (POJO obj : no_cache_prjs) {
                    prjs.set(pids.indexOf(obj.getId()), obj);
                }
        }

        no_cache_ids = null;

        // Check Users
        if (prjs != null && IsAutoLoadUser()) {
            List<Long> no_cache_userids = new ArrayList<Long>();
            String user_cache = "ab";// User.INSTANCE.CacheRegion();
            for (POJO pojo : prjs) {
                if (pojo == null)
                    continue;
                long userid = pojo.GetAutoLoadUser();
                if (userid > 0 && !no_cache_userids.contains(userid)) {
                    POJO user = (POJO) CacheManager.get(user_cache, userid);
                    if (user == null) {
                        no_cache_userids.add(userid);
                    }
                }
            }
            if (no_cache_userids.size() > 0)
                // User.INSTANCE.BatchGet(no_cache_userids);

                no_cache_userids = null;
        }

        return prjs;
    }

    protected boolean IsAutoLoadUser() {
        return false;
    }

    protected long GetAutoLoadUser() {
        return 0L;
    }

    /**
     * 插入对象
     * 
     * @param obj
     * @return 返回插入对象的主键
     */

    private static long _InsertObject(POJO obj) {       
        Map<String, Object> pojo_bean = obj.ListInsertableFields();     
        String[] fields = pojo_bean.keySet().toArray(new String[pojo_bean.size()]);
        
        PreparedStatement ps = null;
        ResultSet rs = null;
        try{
            ps = QueryHelper.getConnection().prepareStatement(_Get_Insert_Sql(obj.TableName(),
                    fields), PreparedStatement.RETURN_GENERATED_KEYS);     
            for(int i = 0, len = fields.length; i < len; i ++){
                ps.setObject(i + 1, pojo_bean.get(fields[i]));
            }
            ps.executeUpdate();
            rs = ps.getGeneratedKeys();
            return rs.next()?rs.getLong(1):-1;
        }catch(SQLException e){
            throw new DBException(e);
        }finally{
            DbUtils.closeQuietly(rs);
            DbUtils.closeQuietly(ps);
            fields = null;
            pojo_bean = null;
        }
    }

    private static String _Get_Insert_Sql(String table, String[] fields) {
        StringBuilder sql = new StringBuilder("INSERT INTO ");
        sql.append(table);
        sql.append('(');
        for(int i = 0, len = fields.length; i < len; i ++){
            if (i > 0) {
                sql.append(',');
            }
            sql.append(fields[i]);
        }
        sql.append(") VALUES(");
        for(int i = 0, len = fields.length; i < len; i ++){
            if (i > 0) {
                sql.append(',');
            }
            sql.append('?');
        }
        sql.append(')');
        return sql.toString();
    }

    /**
     * 列出要插入到数据库的域集合，子类可以覆盖此方法
     * 
     * @return
     */
    protected Map<String, Object> ListInsertableFields() {
        try {
            return _RemoveNoneInsertableFields(BeanUtils.describe(this));
        } catch (Exception e) {
            throw new RuntimeException("Exception when Fetching fields of " + this);
        }
    }
    
    private Map<String, Object> _RemoveNoneInsertableFields(Map<String, Object> props) {
        if (getId() <= 0) {
            props.remove("id");
        }
        props.remove("class");
        props.remove("serialVersionUID");
        
        Field[] fields = _getFields();
        for (Field f : fields) {
            if (!BeanUtils.isCompatibleType(props.get(f.getName()), f.getType())) {
                props.remove(f.getName());
            }
        }
        return props;
    }
    
    private Field[] _getFields() {
        Field[] subFields = getClass().getDeclaredFields();
        Field[] supFields = getClass().getSuperclass().getDeclaredFields();
        Field[] fields = new Field[subFields.length + supFields.length];
        
        for (int i = 0, len = subFields.length; i < len; i ++) {
            fields[i] = subFields[i];
        }
        for (int i = 0, j = subFields.length, len = supFields.length; i < len; i ++) {
            fields[j + i] = supFields[i];
        }
        return fields;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (obj == this)
            return true;
        // 不同的子类尽管ID是相同也是不相等的
        if (!getClass().equals(obj.getClass()))
            return false;
        POJO wb = (POJO) obj;
        return wb.getId() == getId();
    }

    // ----------------------------

    private long id = -999;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

}
